import { _decorator, Collider, Component, Node, Vec3 } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('CrowdAgent')
export class CrowdAgent extends Component {
    // 代理的最大速度
    // @property
    public maxSpeed: number = 5;
    // 代理的当前速度
    public velocity: Vec3 = new Vec3();

    // 代理的目标位置
    public target: Vec3 = new Vec3();

    // 代理的视野半径，用于检测障碍物和邻居
    @property
    public viewRadius: number = 10;

    // 代理的视野角度，用于检测障碍物和邻居
    @property
    public viewAngle: number = Math.PI / 2; // 90 degrees

    // 代理的邻居列表
    public neighbors: CrowdAgent[] = [];

    // 代理的障碍物列表
    public obstacles: Collider[] = [];


    // Seek行为：朝着目标位置移动
    seek(target: Vec3, dt: number): Vec3 {
        // 计算期望速度：从当前位置指向目标位置
        let desiredVelocity = target.clone().subtract(this.node.position).normalize().multiplyScalar(this.maxSpeed);

        // 计算转向力：期望速度减去当前速度
        let steering = desiredVelocity.subtract(this.velocity);

        // 更新速度
        this.velocity.add(steering.multiplyScalar(dt));

        // 限制速度不超过最大速度
        this.velocity.clampf(-this.maxSpeed, this.maxSpeed);

        return this.velocity;
    }

    // 障碍物回避行为
    obstacleAvoidance(dt: number): Vec3 {
        let avoidanceForce = new Vec3();
        let closestObstacle: Collider = null;
        let closestDistance = Infinity;

        // 遍历所有障碍物
        for (let obstacle of this.obstacles) {
            let direction = obstacle.node.position.clone().subtract(this.node.position);
            let distance = direction.length();

            // 检查障碍物是否在视野范围内
            if (distance < this.viewRadius && direction.angle(this.velocity) < this.viewAngle) {
                if (distance < closestDistance) {
                    closestObstacle = obstacle;
                    closestDistance = distance;
                }
            }
        }

        // 如果有障碍物在视野范围内，计算回避力
        if (closestObstacle) {
            let avoidanceDirection = this.node.position.clone().subtract(closestObstacle.node.position).normalize();
            avoidanceForce = avoidanceDirection.multiplyScalar(this.maxSpeed);
        }

        return avoidanceForce;
    }

    // 群体行为：分离、对齐、聚集
    flock(dt: number): Vec3 {
        let separationForce = new Vec3();
        let alignmentForce = new Vec3();
        let cohesionForce = new Vec3();
        let neighborCount = 0;

        // 遍历所有邻居
        for (let neighbor of this.neighbors) {
            let direction = neighbor.node.position.clone().subtract(this.node.position);
            let distance = direction.length();

            // 检查邻居是否在视野范围内
            if (distance < this.viewRadius && direction.angle(this.velocity) < this.viewAngle) {
                neighborCount++;

                // 分离力：远离邻居
                separationForce.add(direction.normalize().multiplyScalar(-1 / distance));

                // 对齐力：与邻居的速度方向一致
                alignmentForce.add(neighbor.velocity);

                // 聚集力：朝着邻居的平均位置移动
                cohesionForce.add(neighbor.node.position);
            }
        }

        // 如果有邻居在视野范围内，计算群体行为力
        if (neighborCount > 0) {
            // 平均分离力
            separationForce.divideScalar(neighborCount);

            // 平均对齐力
            alignmentForce.divideScalar(neighborCount).normalize().multiplyScalar(this.maxSpeed);

            // 平均聚集力
            cohesionForce.divideScalar(neighborCount).subtract(this.node.position).normalize().multiplyScalar(this.maxSpeed);
        }

        return separationForce.add(alignmentForce).add(cohesionForce);
    }

    update(dt: number) {
        // 调用Seek行为并更新代理的位置
        let seekForce = this.seek(this.target, dt);

        // 调用障碍物回避行为并更新代理的位置
        let avoidanceForce = this.obstacleAvoidance(dt);

        // 调用群体行为并更新代理的位置
        let flockForce = this.flock(dt);

        // 组合所有力并更新速度
        let totalForce = seekForce.add(avoidanceForce).add(flockForce);
        this.velocity.add(totalForce.multiplyScalar(dt));

        // 限制速度不超过最大速度
        this.velocity.clampf(-this.maxSpeed, this.maxSpeed);

        // 更新代理的位置
        this.node.setPosition(this.node.position.add(this.velocity.multiplyScalar(dt)));
    }
}